#include "littlefs_port.h"
#include "FlashModule.h"
#include "lfs.h"

/**
 * lfs与底层flash读数据接口
 * @param  c
 * @param  block  块编号
 * @param  off    块内偏移地址
 * @param  buffer 用于存储读取到的数据
 * @param  size   要读取的字节数
 * @return
 */
int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
  W25q_Read(&flash1_obj, c->block_size * block + off, (uint8_t *) buffer, size);
  rt_thread_yield();
  return LFS_ERR_OK;
}

/**
 * lfs与底层flash写数据接口
 * @param  c
 * @param  block  块编号
 * @param  off    块内偏移地址
 * @param  buffer 待写入的数据
 * @param  size   待写入数据的大小
 * @return
 */
int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {

  W25q_Write_Continuous(&flash1_obj, c->block_size * block, (uint8_t *) buffer, size);
  rt_thread_yield();
  return LFS_ERR_OK;
}

/**
 * lfs与底层flash擦除接口
 * @param  c
 * @param  block 块编号
 * @return
 */
int lfs_erase(const struct lfs_config *c, lfs_block_t block) {
  W25q_Erase_1K(&flash1_obj, c->block_size * block);
  rt_thread_yield();
  return LFS_ERR_OK;
}

int lfs_sync(const struct lfs_config *c) {
  rt_thread_yield();
  return LFS_ERR_OK;
}

struct rt_mutex flash1_lock;
int lfs_lock(const struct lfs_config *c) {
  rt_err_t res_flag = RT_EOK;
  int arloie_try = 0;
  // 锁定总线资源
  do {
    res_flag = rt_mutex_take(&flash1_lock, 50 * 2 ^ arloie_try);
    arloie_try++;

    if (arloie_try >= 4) {
      rt_thread_yield();
      return LFS_ERR_INVAL;
    }
  } while (res_flag != RT_EOK);
  rt_thread_yield();
  return LFS_ERR_OK;
}

int lfs_unlock(const struct lfs_config *c) {
  rt_mutex_release(&flash1_lock);
  rt_thread_yield();
  return LFS_ERR_OK;
}


// little fs配置文件
// 注意，每个配置文件只能挂在一个分区。如果想挂多个设备，需要创建对应数量的配置文件和句柄。
const struct lfs_config cfg =
    {
        // block device operations
        .read = lfs_read,
        .prog = lfs_prog,
        .erase = lfs_erase,
        .sync = lfs_sync,
        .lock = lfs_lock,
        .unlock = lfs_unlock,

        // block device configuration
        // 最小读取尺寸
        .read_size = 8,
        // 最小写入尺寸
        .prog_size = 32,
        // 块大小。注意这里的块大小不是flash的块，是文件系统的块。要根据你的flash计算
        // w25q是4096，GT25Q是1024
        .block_size = 1024,
        // 块数量。每个配置文件代表一个分区。因此这里就是一个分区大小，为了方便可以直接设置为整个flash。
        // 单位是块。用单片机总容量除以单个块大小就是总块数
        .block_count = 0x4000,
        .cache_size = 256,
        // 预读取数量，用来预测数据读取，增强读取性能
        .lookahead_size = 256,
        // 均衡算法因数。一般选择中间。1-1000可选。越高读写效率越低，寿命越均衡。-1关闭
        .block_cycles = 512
        //.name_max = 32

        //	//
        //	// 使用静态内存必须设置这几个缓存
        //	//
        //	.read_buffer = read_buffer,
        //	.prog_buffer = prog_buffer,
        //	.lookahead_buffer = lookahead_buffer,
};

// 初始化LFS
void Lfs_Init(void) {
  // 初始化lfs的分区锁
  rt_mutex_init(&flash1_lock, "falsh mutex", RT_IPC_FLAG_PRIO);
}// 初始化LFS

// lfs句柄。注意，这个是用来给测试函数提供测试变量的。如果不要测试，这两个可以跟着测试一起注释掉
lfs_t lfs;
lfs_file_t file;
// entry point
void lfs_test(void) {

  // mount the filesystem
  int err = lfs_mount(&lfs, &cfg);

  // reformat if we can't mount the filesystem
  // this should only happen on the first boot
  if (err) {
    rt_kprintf("mount lfs faild, retry\n");
    err = lfs_format(&lfs, &cfg);
    if (err) {
      rt_kprintf("lfs format faild\n");
      return;
    }else{
		rt_kprintf("lfs format success\n");
		rt_thread_mdelay(1);
	}
    err = lfs_mount(&lfs, &cfg);
    if (err) {
      rt_kprintf("lfs mount retry faild\n");
      return;
    } else {
      rt_kprintf("mount lfs success\n");
    }
  } else {
    rt_kprintf("mount lfs success\n");
  }
  rt_thread_mdelay(1);

  // read current count
  uint32_t boot_count = 0;
  err = lfs_file_open(&lfs, &file, "boot", LFS_O_RDWR | LFS_O_CREAT);
  if (err) {
    rt_kprintf("lfs open file faild, %d\n", err);
    return;
  } else {
    rt_kprintf("lfs open file success\n");
  }
  rt_thread_mdelay(1);
  lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));

  // update boot count
  boot_count += 1;
  lfs_file_rewind(&lfs, &file);
  lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));

  // remember the storage is not updated until the file is closed successfully
  lfs_file_close(&lfs, &file);

  // release any resources we were using
  lfs_unmount(&lfs);

  // print the boot count
  rt_kprintf("boot_count: %d\n", boot_count);
}
